package br.com.afi.util.hibernate;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

import br.com.afi.util.ArraysUtil;
import br.com.afi.util.ReflectionUtil;

/**
 * Essa classe possuí métodos utilitários para a API de criteria do hibernate. 
 * 
 * @author André de Fontana Ignacio
 * @version 1.0
 */
public class CriteriaUtil {
	
	/**
	 * Construtor
	 */
	private CriteriaUtil()
	{
		super();
	}
	
	/**
	 * Constrói critérios do tipo {@link Restrictions#eq(String, Object)} para todos os
	 * relacionamentos do objeto informado que não forem nulos.
	 * 
	 * @param entity Entidade
	 * @param excludeProperty Propriedades que seram deconsideradas
	 * @return Critérios
	 */
	@SuppressWarnings("unchecked")
	public static List<Criterion> buildRelationshipsCriterions( Object entity, String... excludeProperty )
	{
		final List<Criterion> criterions = new ArrayList<Criterion>();
		final Field[] fields = ReflectionUtil.getDeclaredFields( entity );
		final ArraysUtil<String> arrayUtil = new ArraysUtil<String>();

		for ( Field field : fields )
		{
			final boolean annotation = ReflectionUtil.isAnnotation( field, ManyToOne.class ) || ReflectionUtil.isAnnotation( field, OneToMany.class );
			if ( annotation )
			{
				final String fieldName = field.getName();
				if ( excludeProperty == null || !arrayUtil.contains( excludeProperty, fieldName ) )
				{
					try
					{
						Object obj = null;
						try
						{
							obj = ReflectionUtil.invokeGetterMethod( entity, field );
						}
						catch ( Exception e )
						{
						}
						if ( obj != null )
						{
							boolean ok = true;
							if ( obj instanceof Collection )
							{
								final Collection c = ( Collection ) obj;
								ok = !c.isEmpty();
							}
							if ( ok )
							{
								criterions.add( Restrictions.eq( field.getName(), obj ) );
							}
						}
					}
					catch ( Exception e )
					{
						final RuntimeException ex = new RuntimeException( "Erro ao gerar os critérios para os relacionamentos", e );
						throw ex;
					}
				}
			}
		}
		return criterions;
	}
}
